package org.biogroovy.io.eutils

import javax.xml.namespace.QName
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.xpath.*

import org.biogroovy.conf.BioGroovyConfig
import org.biogroovy.eutils.EUtilsURLFactory
import org.biogroovy.io.AbsXmlReader
import org.biogroovy.io.IFetcher;
import org.biogroovy.models.*
import org.w3c.dom.Node


/**
 * This reader produces Transcript objects by reading GBSeqTranscript responses 
 * from EUtils.
 * 
 */
class GBSeqTranscriptReader extends AbsXmlReader<Transcript>{
	
	/** The database name. */
	private static final DATABASE_NAME = "gbseqtranscript"


	/**
	 * A map of XPath expressions.  The keys represent fields in the Transcript class,
	 * the keys represent XPath expressions.
	 */
	static final Map<String, String> XPATH_MAP = [
		accession:'/GBSet/GBSeq/GBSeq_primary-accession',
		sequence:'/GBSet/GBSeq/GBSeq_sequence',
		species:'/GBSet/GBSeq/GBSeq_organism',
		name:'/GBSet/GBSeq/GBSeq_definition'
	];

	static final Map<String, QName> NODE_TYPE_MAP = [
		accession:XPathConstants.STRING,
		sequence:XPathConstants.STRING,
		species:XPathConstants.STRING,
		name:XPathConstants.STRING
	]

	static final String ROOT_PATH = "//GBSet/GBSeq";
	
	String tool = null;
	String email = null;
	
	/**
	 * Constructor.
	 */
	public GBSeqTranscriptReader(){
		this.databaseName = DATABASE_NAME;
		ConfigObject conf = BioGroovyConfig.getConfig();
		this.tool = conf.eutils.tool
		this.email = conf.eutils.email
	}

	/**
	 * This method retrieves an GBSeq Transcript record and parses the contents.
	 * @param id  The ID of the GBSeq Transcript record to be retrieved and parsed.
	 * @throws IOException if there is a problem retrieving or parsing the file.
	 */
	public Transcript read(String id) throws IOException{
		URL url = getUrl(id, [tool:this.tool, email:this.email])
		return read(url.openStream());
	}

	/**
	 * This method reads an input stream containing a GBSeq Transcript.
	 * @param is  The input stream.
	 */
	public Transcript read(InputStream is) throws IOException{
		Transcript transcript = new Transcript();
		def builder  = DocumentBuilderFactory.newInstance().newDocumentBuilder();
		def root     = builder.parse(is).documentElement

		parseData(root,transcript, XPATH_MAP, NODE_TYPE_MAP)

		builder = null;
		root = null;

		return transcript;
	}

	/**
	 * This convenience method reads a file containing a GBSeq Transcript.
	 * @param file  The file containing the GBSeq XML record for a Transcript.
	 */
	public Transcript read(File file) throws IOException{
		if (!file.exists()){
			throw new FileNotFoundException("The file was not found: ${file.getName()}")
		}
		return read(new FileInputStream(file));
	}
	
	/**
	 * Reads a list of Transcripts from the input stream.
	 * @param inputStream
	 * @return a list of Transcript
	 */
	public List<Transcript> readList(InputStream inputStream) throws IOException{
		List<Transcript> geneList = new ArrayList<Transcript>();
		
		def builder  = DocumentBuilderFactory.newInstance().newDocumentBuilder();
		def root     = builder.parse(inputStream).documentElement
		
		XPath xpath = XPathFactory.newInstance().newXPath();
		NodeList nodeList = xpath.evaluate(ROOT_PATH, root, XPathConstants.NODESET);
		for(int i=0; i < nodeList.getLength(); i++){
			Node node = nodeList.item(i)
			Transcript gene = parse(node);
			geneList.add(gene);
		}
		
		builder = null;
		root = null;
				
		return geneList;
	}

	@Override
	public void parse(Transcript gene, Node node) {
		parseData(node, gene, XPATH_MAP, NODE_TYPE_MAP);
	}

	@Override
	public Transcript fetch(String id) throws IOException {
		URL url = getUrl(id, [tool:this.tool, email:this.email])
		return read(url.openStream())
	}

	@Override
	public URL getUrl(String id, Map<String, String> paramMap) {
		Map<String, String> map = [db:EUtilsURLFactory.DB_NUCLEOTIDE, id:id, rettype:'gb', retmode:'xml']
		map.putAll(paramMap)
		
		String url = EUtilsURLFactory.getURL(EUtilsURLFactory.EFETCH, map);
		return new URL(url)
	}


	@Override
	public List<Transcript> fetchAll(String id) throws IOException {
		URL url = getUrl(id,[tool:this.tool, email:this.email]);
		return readList(url.openStream())
	}

	@Override
	public IFetcher<Transcript> getNewInstance() {
		return new GBSeqTranscriptReader();
	}
}

